﻿using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;
using TA.JSON;
using System.IO;
using System.Web.UI;
using System.ComponentModel;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Xml;
using System.Collections.Specialized;
using System.Reflection;
using System.Threading;
using System.Collections;
using TA.Turbo.Controls;
using TA.DotNetUtils;

namespace TA.Turbo
{
	public partial class TPage
	{
		#region VARS
		bool? _isValid, _javaScript;
		PostScenario _currentScenario;
		BaseValidator[] _currentValidators;
		int _loadIteration = 0;
		bool _loadAgain = true;
		List<PostScenario> _temporaryScenarios = new List<PostScenario>();
		IEventHandler _eventTarget;
		string _eventName;
		StateBag _controlState = new StateBag();
		Dictionary<string, object> _controlStateForReset = new Dictionary<string, object>();
		Dictionary<string, string> _ajaxScripts = new Dictionary<string, string>();
		//AjaxResult _ajaxResult = new AjaxResult();
		//Dictionary<IClientId, bool> _ajaxClientIdControls = new Dictionary<IClientId, bool>();
		#endregion
		#region METHODS
		/// <summary>
		/// Adds a temporary scenario for this request only. This is used when using dynamic control creation. Call this during Load stage to ensure that all necessary controls are populated.
		/// </summary>
		/// <param name="ps">The scenario to be added for this request only.</param>
		internal void AddScenario(PostScenario ps)
		{
			this._temporaryScenarios.Add(ps);
			if (ps.Parent == null)
				this._specialContainer.Controls.Add(ps);
		}
		/// <summary>
		/// Processes an HTTP request.
		/// </summary>
		/// <param name="context"></param>
		public override void ProcessRequest(HttpContext context)
		{
#if DEBUG
			LogBegin();
			Log("TPage.ProcessRequest BEGIN");
#endif

			try
			{
				this.ProcessRequest_SetIntrinsics(context);

				if (!this._initDone)
				{
					this.InitPage();
				}
				else
				{
					this.ProcessRequest_Reset();
				}

				this.ProcessRequest_DetermineJavaScript();

				this.Form.Action = this.Request.RawUrl;

				this.ProcessRequest_DeterminePostBack();

				this.ProcessRequest_Load();

				this.ProcessRequest_Event();

				this.ProcessRequest_JSCookie();

				HtmlTextWriter writer = new HtmlTextWriter(this.Response.Output);
				this.RenderControl(writer);

				this.OnUnload(EventArgs.Empty);
			}
			catch (ThreadAbortException ex)
			{
				if (ex.ExceptionState != null && ex.ExceptionState.GetType().Name.Contains("CancelModuleException"))
				{
					if (!string.IsNullOrEmpty(this.Response.RedirectLocation))
					{
						if (this.CurrentScenario != null && this.CurrentScenario.PostMode == PostMode.Ajax)
						{
							this.Response.StatusCode = 200;
							this.Response.Write("#####302");
							this.Response.Write(this.Response.RedirectLocation);
						}
					}
				}
			}
			catch (Exception ex)
			{
#if DEBUG
				Log("TPage.ProcessRequest ERROR:");
				Log(ex.ToString());
#else
#endif
				throw;
				//this.Response.Write(ex.ToString());
			}
			finally
			{
#if DEBUG
				Log("TPage.ProcessRequest END");
				LogEnd();
#endif
			}
		}

		private void ProcessRequest_DetermineJavaScript()
		{
			if (this.Request.Cookies["JS"] != null)
				this._javaScript = true;
			else if (this.Request.Cookies["NOJS"] != null)
				this._javaScript = false;
			else
				this._javaScript = null;

			this._scriptIncludesControl.Visible = this._javaScript != false;
			//this._jsInitControl.Visible = this._javaScript != true;
			//this._litLinksInit.Visible = this._javaScript != true;
		}
		private void ProcessRequest_SetIntrinsics(HttpContext context)
		{
			mSetIntrinsics.Invoke(this, new object[] { context });
			f_items.SetValue(this, HttpContext.Current.Items);
#if DEBUG
			Log("TPage.ProcessRequest SetIntrinsics DONE");
#endif
		}
		#region INIT
		#region RE-USE
		private void ProcessRequest_Reset()
		{
#if DEBUG
			Log("TPage.ProcessRequest_Reset BEGIN");
#endif

			this.Reset();
			foreach (TControl ctl in this._controls.Values)
			{
				ctl.Reset();
			}
#if DEBUG
			Log("TPage.ProcessRequest_Reset Reset DONE");
#endif

			f_fOnFormRenderCalled.SetValue(this, false);
#if DEBUG
			Log("TPage.ProcessRequest_Reset fOnFormRenderCalled DONE");
#endif

			this.ResultMain = this.ResultCustom = this.ResultHashString = null;
			this.SerializeResult = null;
			this._loadIteration = 0;
			this._loadAgain = true; // first time
			this._isValid = null;
			this._currentValidators = null;
			this._eventTarget = null;
			this._eventName = null;
			//this._ajaxResult = new AjaxResult();
			this._ajaxScripts = new Dictionary<string, string>(); // creating new one to re-init ordering
			//this._ajaxClientIdControls.Clear();

			if (this._currentScenario != null)
			{
				this._currentScenario.Parent.Controls.Remove(this._currentScenario);
				this._currentScenario = null;
			}

			while (this._temporaryScenarios.Count > 0)
			{
				PostScenario ps = this._temporaryScenarios[this._temporaryScenarios.Count - 1];
				ps.Parent.Controls.Remove(ps);
				this._temporaryScenarios.Remove(ps);
			}

#if DEBUG
			Log("TPage.ProcessRequest_Reset END");
#endif
		}

		/// <summary>
		/// Resets the state of this control.
		/// </summary>
		private void Reset()
		{
			this._controlState.Clear();

			foreach (KeyValuePair<string, object> kvp in this._controlStateForReset)
			{
				this._controlState[kvp.Key] = kvp.Value;
			}
		}
		#endregion
		#endregion
		#region LOAD
		private void ProcessRequest_DeterminePostBack()
		{
			NameValueCollection nvc = this.Request.Form;
			if (CurrentScenario != null && this.CurrentScenario.HttpVerb == HttpVerb.Get)
				nvc = this.Request.QueryString;
			if (nvc.Count > 0)
				f_requestValueCollection.SetValue(this, nvc);
			else
				f_requestValueCollection.SetValue(this, null);
		}
		private void ProcessRequest_Load()
		{
			if (this.IsTurboPost || this.IsFormPost)
			{
				while (this._loadAgain)
				{
					this._loadAgain = false;

					this.ProcessRequest_Load_State();
					this.OnLoad(EventArgs.Empty);

					++this._loadIteration;
				}
			}
			else
			{
				this.OnLoad(EventArgs.Empty);
			}
		}
		private void ProcessRequest_Load_State()
		{
#if DEBUG
			Log("TPage.ProcessRequest_Load_State BEGIN");
#endif

			if (this.CurrentScenario == null) return;

			this.ProcessRequest_Load_State_View();

			this.ProcessRequest_Load_State_Control();

#if DEBUG
			Log("TPage.ProcessRequest_Load_State END");
#endif
		}
		private void ProcessRequest_Load_State_View()
		{
#if DEBUG
			Log("TPage.ProcessRequest_Load_State_View BEGIN");
#endif

			NameValueCollection nvc = this.CurrentScenario.HttpVerb == HttpVerb.Get ? this.Request.QueryString : this.Request.Form;
			foreach (BaseControlReference cr in this.CurrentScenario.GetAllPostControls())
			{
				TControl bc = cr.Control as TControl;
				if (bc != null)
				{
					bc.LoadState(cr, nvc);
				}
			}

#if DEBUG
			Log("TPage.ProcessRequest_Load_State_View END");
#endif
		}
		private void ProcessRequest_Load_State_Control()
		{
#if DEBUG
			Log("TPage.ProcessRequest_Load_State_Control BEGIN");
#endif

			this.Base64ToControlState(this.Request["__VIEWSTATE"]);

#if DEBUG
			Log("TPage.ProcessRequest_Load_State_Control END");
#endif
		}
		private void ProcessRequest_Event()
		{
#if DEBUG
			Log("TPage.ProcessRequest_Event_Turbo BEGIN");
#endif

			if (this.IsTurboPost || this.IsFormPost)
			{
				if (IsValid)
				{
					if (this.EventTarget != null && !string.IsNullOrEmpty(this.EventName))
					{
						this.EventTarget.RaiseEvent(this.EventName, this.EventArgument);
					}
				}
			}

#if DEBUG
			Log("TPage.ProcessRequest_Event_Turbo END");
#endif
		}
		private void ProcessRequest_JSCookie()
		{
			if (!this.IsTurboPost)
			{
				//if (this.Request.Cookies["JS"] != null)
				//  this.Response.Cookies.Add(new HttpCookie("JS", "0") { Expires = DateTime.Now.AddYears(-5) });
				if (this.Request.Cookies["JS"] == null && this.Request.Cookies["NOJS"] == null)
					this.Response.Cookies.Add(new HttpCookie("NOJS", "0") { Expires = DateTime.Now.AddYears(1) });
			}
		}
		#endregion
		#region RENDER
		/// <summary>
		/// Renders the page. Depending on the kind of request, outputs JSON response or renders the page.
		/// </summary>
		/// <param name="writer"></param>
		public override void RenderControl(System.Web.UI.HtmlTextWriter writer)
		{
#if DEBUG
			Log("TurboPage.RenderControl BEGIN");
#endif


			if (IsTurboPost && CurrentScenario != null && CurrentScenario.PostMode == PostMode.Ajax)
			{
				RenderControl_Ajax(writer);
			}
			else
			{
				RenderControl_Page(writer);
			}

#if DEBUG
			Log("TurboPage.RenderControl END");
#endif
		}
		private void RenderControl_Ajax(HtmlTextWriter writer)
		{
#if DEBUG
			Log("TurboPage.RenderControl_Ajax BEGIN");
#endif

			Response.AddHeader("Connection", "close");

			JsonWriter jw = new JsonWriter(writer);
			jw.ExternalStringsVariable = this.Obfuscator.TranslateId("__JE");

			jw.StartObject();

			if (this.IsValid)
			{
				this.RenderControl_Ajax_Results(jw);
			}
			else
			{
				this.RenderControl_Ajax_Validators(jw);
			}

			jw.EndObject();

			foreach (string s in jw.ExternalStrings)
			{
				writer.Write("<!>");
				writer.Write(s);
			}

#if DEBUG
			Log("TurboPage.RenderControl_Ajax END");
#endif
		}
		private void RenderControl_Ajax_Results(JsonWriter jw)
		{
#if DEBUG
			Log("TurboPage.RenderControl_Ajax_Results BEGIN");
#endif

			this.RenderControl_Ajax_Results_ControlState(jw);
			this.RenderControl_Ajax_Results_HashString(jw);

			Tuple<List<BaseControlReference>, List<UpdatePanel>> arc = this.CurrentScenario.GetAllReturnControls();
#if DEBUG
			Log("TurboPage.RenderControl_Ajax_Results GetAll DONE");
#endif

			this.RenderControl_Ajax_Results_Controls(jw, arc.Item0);
			this.RenderControl_Ajax_Results_UpdatePanels(jw, arc.Item1);

			this.RenderControl_Ajax_Results_Result(jw, this.ResultMain, "M");
#if DEBUG
			Log("TurboPage.RenderControl_Ajax_Results Main DONE");
#endif

			this.RenderControl_Ajax_Results_Result(jw, this.ResultCustom, "C");
#if DEBUG
			Log("TurboPage.RenderControl_Ajax_Results Custom DONE");
#endif

			this.RenderControl_Ajax_Results_Scenarios(jw);
			this.RenderControl_Ajax_Results_Scripts(jw, arc.Item1);

#if DEBUG
			Log("TurboPage.RenderControl_Ajax_Results END");
#endif
		}
		private void RenderControl_Ajax_Results_ControlState(JsonWriter jw)
		{
			jw.PropertyName("E");
			jw.Value(this.ControlStateToBase64());
		}
		private void RenderControl_Ajax_Results_HashString(JsonWriter jw)
		{
			if (this.CurrentScenario.ReturnHashString)
			{
				if (this.ResultHashString == null) // if not set explicitly
				{ // build from providers
					StringBuilder sbHash = new StringBuilder();
					foreach (IHashStringPartProvider provider in this.HashStringProviders)
					{
						if (!string.IsNullOrEmpty(provider.HashStringPart))
						{
							if (sbHash.Length > 0) sbHash.Append("&");
							sbHash.Append(provider.HashStringPart);
						}
					}
					ResultHashString = sbHash.ToString();
				}

				jw.PropertyName("H");
				jw.Value(this.ResultHashString);
			}
#if DEBUG
			Log("TurboPage.RenderControl_Ajax_Results Hash DONE");
#endif
		}
		private void RenderControl_Ajax_Results_Controls(JsonWriter jw, List<BaseControlReference> list)
		{
			if (list.Count > 0)
			{
				// start writing stuff
				jw.PropertyName("O");
				jw.StartArray();
				foreach (BaseControlReference cr in list)
				{
					if (cr.Anything)
					{
						TControl baseCtl = cr.Control as TControl;
						if (baseCtl != null)
						{
							baseCtl.SaveState(jw, cr);
						}
					}
				}
				jw.EndArray();
			}
#if DEBUG
			Log("TurboPage.RenderControl_Ajax_Results RetCtls DONE");
#endif
		}
		private void RenderControl_Ajax_Results_UpdatePanels(JsonWriter jw, List<UpdatePanel> list)
		{
			if (list.Count > 0)
			{
				jw.PropertyName("U");
				jw.StartArray();
				foreach (UpdatePanel up in list)
				{
					up.Serialize(jw);					
				}
				jw.EndArray();
			}
#if DEBUG
			Log("TurboPage.RenderControl_Ajax_Results Upd DONE");
#endif
		}
		private void RenderControl_Ajax_Results_Scenarios(JsonWriter jw)
		{
			if (this._temporaryScenarios.Count > 0)
			{
				jw.PropertyName("S");
				jw.StartArray();

				foreach (PostScenario ps in this._temporaryScenarios)
				{
					ps.Serialize(jw);
				}

				jw.EndArray();
			}
#if DEBUG
			Log("TurboPage.RenderControl_Ajax_Results Scenarios DONE");
#endif
		}
		private void RenderControl_Ajax_Results_Result(JsonWriter jw, object result, string propShortName)
		{
#if DEBUG
			Log("TurboPage.RenderControl_Ajax_OutputResult BEGIN");
#endif

			if (result != null)
			{
				jw.PropertyName(propShortName);
				try
				{
					jw.Value(result);
				}
				catch
				{
					if (SerializeResult != null)
					{
						SerializeEventArgs sea = new SerializeEventArgs { Writer = jw, Entry = result };
						SerializeResult(this, sea);
					}
					else
					{
						throw new InvalidOperationException("Failed to output the result. The result is not serializable and there is no serializer attached.");
					}
				}
			}

#if DEBUG
			Log("TurboPage.RenderControl_Ajax_OutputResult END");
#endif
		}
		private void RenderControl_Ajax_Results_Scripts(JsonWriter jw, IEnumerable<UpdatePanel> updatePanels)
		{
			List<IClientId> redrawn = new List<IClientId>();
			foreach (IClientId clientId in this.AllClientIds)
			{
				foreach(UpdatePanel up in updatePanels)
				{
					if (clientId.Control.Visible && up.IsAscendantOf(clientId.Control))
					{
						redrawn.Add(clientId);
						break;
					}
				}				
			}
			
			if (this._ajaxScripts.Count + redrawn.Count > 0)
			{
				jw.PropertyName("R");
				jw.StartArray();

				foreach (IClientId clientId in redrawn)
				{
					jw.Value(TPage.PrepareScript(clientId.GetScript()));
				}
				foreach (string script in this._ajaxScripts.Values)
				{
					jw.Value(script);
				}

				jw.EndArray();
			}
		}
		private void RenderControl_Ajax_Validators(JsonWriter jw)
		{
#if DEBUG
			Log("TurboPage.RenderControl_Ajax_Validators BEGIN");
#endif

			jw.PropertyName("V");
			jw.StartArray();

			foreach (BaseValidator v in this.CurrentValidators)
			{

				jw.StartObject();

				jw.PropertyName("I");
				jw.Value(v.ClientID);

				jw.PropertyName("V");
				jw.Value(v.IsValid);

				jw.EndObject();
			}

			jw.EndArray();

#if DEBUG
			Log("TurboPage.RenderControl_Ajax_Validators END");
#endif
		}
		private void RenderControl_Page(HtmlTextWriter writer)
		{
#if DEBUG
			Log("TurboPage.RenderControl_Page BEGIN");
#endif

			this.RenderControl_Page_ControlState();

			this.RenderControl_Page_UniqueCookies();

			base.RenderControl(writer);



#if DEBUG
			if (this.OptimizeScripts)
			{
				writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
				writer.RenderBeginTag(HtmlTextWriterTag.Script);

				writer.Write("var Identifiers = ");
				JsonWriter jw = new JsonWriter(writer);
				jw.StartObject();

				foreach (string key in this._obfuscator._revIdentifiers.Keys)
				{
					jw.PropertyName(key);
					jw.Value(this._obfuscator._revIdentifiers[key]);
				}

				jw.EndObject();

				writer.RenderEndTag();
			}
#endif


#if DEBUG
			Log("TurboPage.RenderControl_Page END");
#endif
		}
		private void RenderControl_Page_ControlState()
		{
			TPage.p_ClientState.SetValue(this, this.ControlStateToBase64(), null);
			//this.RegisterHiddenField("__VIEWSTATE", );
		}
		private void RenderControl_Page_UniqueCookies()
		{
#if DEBUG
			Log("TurboPage.RenderControl_Page_UniqueCookies BEGIN");
#endif

			if (this._uniqueCookies.Count > 0)
			{
				StringBuilder sb = new StringBuilder();
				sb.Append(this.Obfuscator.Obfuscate("TA.Turbo.Common.InitCookies(", this.OptimizeScripts));

				JsonWriter jw = new JsonWriter(sb);

				jw.StartObject();

				foreach (string uniqueCookie in this._uniqueCookies)
				{
					jw.PropertyName(uniqueCookie);
					jw.Null();
				}

				jw.EndObject();

				sb.Append(");");

				ClientScript.RegisterClientScriptBlock(this.GetType(), "Turbo_UniqueCookies", sb.ToString(), true);
			}

#if DEBUG
			Log("TurboPage.RenderControl_Page_UniqueCookies END");
#endif
		}
		#endregion
		#endregion
	}
}